package top.ruanlitao.shortlink.project.service.idgen;

import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import top.ruanlitao.shortlink.project.common.constant.RedisKeyConstant;

import java.util.concurrent.atomic.AtomicLong;

@Slf4j
@Component
@RequiredArgsConstructor
public class IdGenerator {

    private final StringRedisTemplate redisTemplate;
    private final RedissonClient redissonClient;

    @Value("${short-link.id-generator.block-size:10000}")
    private int blockSize;

    // 当前范围 [rangeStart, rangeEnd)
    // private AtomicLong rangeStart;
    private AtomicLong rangeEnd;
    private AtomicLong currId;

    @PostConstruct
    public void init() {
        this.rangeEnd = new AtomicLong(-1);
        this.currId = new AtomicLong(0);
        log.info("ID Generator initialized with key '{}' and block size {}", RedisKeyConstant.GENERATE_ID_KEY, blockSize);
        // 预加载 ID
        // 获取分布式锁
        RLock lock = redissonClient.getLock(RedisKeyConstant.LOCK_GENERATE_ID_KEY);
        lock.lock();
        try {
            requestNewRangeFromRedis();
        } finally {
            lock.unlock();
        }

    }

    /**
     * 获取下一个唯一的 ID，此方法是线程安全。
     * @return 唯一的 ID
     */
    public long getNextId() {

        /*if (currId.get() >= rangeEnd.get()) {
            synchronized (this) {
                // Double-check
                if (currId.get() >= rangeEnd.get()) {
                    requestNewRangeFromRedis();
                }
            }
        }

        return currId.getAndIncrement();*/

        // 使用无限循环来确保总能获取到ID
        for (;;) {
            long current = currId.get();
            long end = rangeEnd.get();

            // Fast-path: 如果当前ID在范围内
            if (current < end) {
                // 使用CAS操作尝试原子性地增加ID
                // 如果成功，说明我们成功“抢”到了这个ID，直接返回
                if (currId.compareAndSet(current, current + 1)) {
                    //log.info("[Green Channel] Successfully fetched ID: {}", current);
                    return current;
                }
                // 如果CAS失败，说明有其他线程抢先修改了localId，
                // 循环会继续，在下一次迭代中尝试获取新的ID值。
            } else {
                // Slow-path: 本地ID耗尽，需要从Redis获取新号段
                // 使用同步块确保只有一个线程去请求Redis
                synchronized (this) {
                    // Double-Checked Locking: 再次检查，防止在等待锁的过程中，
                    // 其他线程已经获取了新的号段。
                    if (rangeEnd.get() == end) {
                        requestNewRangeFromRedis();
                    }
                    // 请求完新号段后，不在此处返回值，而是让循环继续，
                    // 在下一次迭代中通过fast-path来安全地获取ID。
                }
            }
        }
    }

    /**
     * 蓝色频道：从 Redis 获取新的 ID 段
     */
    private void requestNewRangeFromRedis() {
        log.info("[Blue Channel] Local ID range exhausted. Fetching new range from Redis...");
        try {
            // 原子地从 redis 中递增全局计数器，块大小
            Long newRangeEnd = redisTemplate.opsForValue().increment(RedisKeyConstant.GENERATE_ID_KEY, blockSize);
            if (newRangeEnd == null) {
                throw new IllegalStateException("Could not fetch new ID range from Redis. INCRBY returned null.");
            }
            long newRangeStart = newRangeEnd - blockSize;

            // 更新本地状态
            this.currId.set(newRangeStart);
            this.rangeEnd.set(newRangeEnd);

            log.info("[Blue Channel] Successfully fetched new ID range: [{}, {})", newRangeStart, newRangeEnd);
        } catch (Exception e) {
            log.error("[Blue Channel] Critical error: Failed to fetch new ID range from Redis!", e);
            // In a real production system, you might have fallback mechanisms here
            throw new RuntimeException("Failed to get new ID range from Redis", e);
        }
    }
}